﻿using System;

namespace Test30.Util
{
    public struct Character : IEquatable<Character>
    {
        public static TermColor DefaultForeColor => TermColor.White;
        public static TermColor DefaultBackColor => TermColor.Black;
        public bool IsWhitespace => _glyph == Glyph.Space;
        public TermColor BackColor => _backColor;
        public TermColor ForeColor => _foreColor;
        public Glyph Glyph => _glyph;

        public static Glyph ToGlyph(char ascii)
        {
            return (Glyph)(ascii - 32);
        }
        public static Glyph ParseGlyph(string text)
        {
            if (text.Length == 1)
            {
                // a single character is assumed to be ascii
                return ToGlyph(text[0]);
            }
            else
            {
                // multiple characters are the glyph enum names
                return (Glyph)Enum.Parse(typeof(Glyph), text, true);
            }
        }
        public static Character Parse(string text)
        {
            if (text == null) throw new ArgumentNullException($"text");
            if (text.Length == 0) throw new ArgumentException("Argument 'text' cannot be empty.");

            text = text.Trim();

            // separate out the colors and glyph
            string[] parts = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            // only supports three parts (max)
            if (parts.Length > 3) throw new ArgumentException("Character.Parse() should be formatted \"Glyph\", \"ForeColor Glyph\", or \"ForeColor BackColor Glyph\".");

            TermColor foreColor = DefaultForeColor;
            TermColor backColor = DefaultBackColor;

            // parse the glyph
            Glyph glyph = ParseGlyph(parts[parts.Length - 1]);

            // parse the fore color
            if (parts.Length > 1)
            {
                foreColor = TermColors.FromName(parts[0]);
            }

            // parse the back color
            if (parts.Length > 2)
            {
                backColor = TermColors.FromName(parts[1]);
            }

            return new Character(glyph, foreColor, backColor);
        }
        public Character(Glyph glyph, TermColor foreColor, TermColor backColor)
        {
            _glyph = glyph;
            _backColor = backColor;
            _foreColor = foreColor;
        }

        public Character(Glyph glyph, TermColor foreColor)
            : this(glyph, foreColor, DefaultBackColor)
        {
        }

        public Character(Glyph glyph)
            : this(glyph, DefaultForeColor)
        {
        }

        public Character(char ascii, TermColor foreColor, TermColor backColor)
            : this(ToGlyph(ascii), foreColor, backColor)
        {
        }

        public Character(char ascii, TermColor foreColor)
            : this(ToGlyph(ascii), foreColor, DefaultBackColor)
        {
        }

        public Character(char ascii)
            : this(ToGlyph(ascii), DefaultForeColor)
        {
        }

        #region Equals and GetHashCode implementation
        public override bool Equals(object obj)
        {
            if (obj is Character) return Equals((Character)obj);
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            int hashCode = 0;
            unchecked
            {
                hashCode += 1000000007 * _glyph.GetHashCode();
                hashCode += 1000000009 * _foreColor.GetHashCode();
                hashCode += 1000000021 * _backColor.GetHashCode();
            }
            return hashCode;
        }

        public static bool operator ==(Character lhs, Character rhs)
        {
            return lhs.Equals(rhs);
        }

        public static bool operator !=(Character lhs, Character rhs)
        {
            return !(lhs == rhs);
        }
        #endregion

        #region IEquatable<Character> Members
        public bool Equals(Character other)
        {
            return (_glyph == other._glyph) && _backColor.Equals(other._backColor) && _foreColor.Equals(other._foreColor);
        }
        #endregion

        private readonly Glyph _glyph;
        private readonly TermColor _foreColor;
        private readonly TermColor _backColor;
    }

}
